using System;
using System.Collections.Generic;
using System.Text;
using OnlinePlots.Diagrams;
using System.Collections.ObjectModel;
using System.Runtime.Serialization;
using System.Windows.Shapes;
using System.Windows.Media;
using System.Windows.Controls;
using System.Windows;
using System.Windows.Data;

namespace OnlinePlots.Plots.Elements
{
    /// <summary>
    /// Represents a grid element of the plot
    /// </summary>
    public class GridElement : DiagramElement
    {
        protected Line axisLine;
        protected List<Line> auxLines = new List<Line>();
        protected List<Line> basicLines = new List<Line>();

        /// <summary>
        /// Creates a new instance of grid element
        /// </summary>
        public GridElement()
            : base()
        {
        }

        new public static readonly DependencyProperty ZOrderProperty = DependencyProperty.Register(
                "ZOrder",
                typeof(int),
                typeof(GridElement),
                new PropertyMetadata(100)
            );

        public static readonly DependencyProperty BasicLineBrushProperty = DependencyProperty.Register(
                        "BasicLineBrush",
                        typeof(Brush),
                        typeof(GridElement),
                        new PropertyMetadata(new SolidColorBrush(Colors.Gray), new PropertyChangedCallback(OnBasicLineBrushPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets pen of basic line of the plot
        /// </summary>
        public Brush BasicLineBrush
        {
            get { return (Brush)GetValue(BasicLineBrushProperty); }
            set { SetValue(BasicLineBrushProperty, value); }
        }

        static protected void OnBasicLineBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("BasicLineBrush");
        }

        public static readonly DependencyProperty AxisLineBrushProperty = DependencyProperty.Register(
                        "AxisLineBrush",
                        typeof(Brush),
                        typeof(GridElement),
                        new PropertyMetadata(new SolidColorBrush(Colors.Black), new PropertyChangedCallback(OnAxisLineBrushPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets pen of axis line of the plot
        /// </summary>
        public Brush AxisLineBrush
        {
            get { return (Brush)GetValue(BasicLineBrushProperty); }
            set { SetValue(BasicLineBrushProperty, value); }
        }

        static protected void OnAxisLineBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("AxisLineBrush");
        }

        public static readonly DependencyProperty AuxLineBrushProperty = DependencyProperty.Register(
                        "AuxLineBrush",
                        typeof(Brush),
                        typeof(GridElement),
                        new PropertyMetadata(new SolidColorBrush(Colors.Gray), new PropertyChangedCallback(OnAuxLineBrushPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets pen of auxilluary line of the plot
        /// </summary>
        public Brush AuxLineBrush
        {
            get { return (Brush)GetValue(AuxLineBrushProperty); }
            set { SetValue(AuxLineBrushProperty, value); }
        }

        static protected void OnAuxLineBrushPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("AuxLineBrush");
        }

        public static readonly DependencyProperty DesiredLineDensityProperty = DependencyProperty.Register(
                        "DesiredLineDensity",
                        typeof(int),
                        typeof(GridElement),
                        new PropertyMetadata(10, new PropertyChangedCallback(OnDesiredLineDensityPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets the desired density of basic lines
        /// </summary>
        public int DesiredLineDensity
        {
            get { return (int)GetValue(DesiredLineDensityProperty); }
            set { SetValue(DesiredLineDensityProperty, value); }
        }

        static protected void OnDesiredLineDensityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("DesiredLineDensity");
        }

        public static readonly DependencyProperty DesiredAuxLineDensityProperty = DependencyProperty.Register(
                        "DesiredAuxLineDensity",
                        typeof(int),
                        typeof(GridElement),
                        new PropertyMetadata(2, new PropertyChangedCallback(OnDesiredAuxLineDensityPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets the desired density of auxilluary lines
        /// </summary
        public int DesiredAuxLineDensity
        {
            get { return (int)GetValue(DesiredAuxLineDensityProperty); }
            set { SetValue(DesiredAuxLineDensityProperty, value); }
        }

        static protected void OnDesiredAuxLineDensityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("DesiredAuxLineDensity");
        }

        public static readonly DependencyProperty DrawGridFromAxisLineProperty = DependencyProperty.Register(
                        "DrawGridFromAxisLine",
                        typeof(Boolean),
                        typeof(GridElement),
                        new PropertyMetadata(true, new PropertyChangedCallback(OnDrawGridFromAxisLinePropertyChanged))
                    );

        /// <summary>
        /// Gets or sets the flag indicating whether grid should be drawn from
        /// axis line
        /// </summary>
        public Boolean DrawGridFromAxisLine
        {
            get { return (bool)GetValue(DrawGridFromAxisLineProperty); }
            set { SetValue(DrawGridFromAxisLineProperty, value); }
        }

        static protected void OnDrawGridFromAxisLinePropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("DrawGridFromAxisLine");
        }

        /// <summary>
        /// Gets axis line shape of the grid
        /// </summary>
        public Line AxisLine
        {
            get { return axisLine; }
        }

        /// <summary>
        /// Gets array of basic line shapes of the grid
        /// </summary>
        public ReadOnlyCollection<Line> BasicLines
        {
            get { return basicLines.AsReadOnly(); }
        }

        /// <summary>
        /// Gets array of auxilluary line shapes of the grid
        /// </summary>
        public ReadOnlyCollection<Line> AuxLines
        {
            get { return auxLines.AsReadOnly(); }
        }

        public static readonly DependencyProperty OpacityProperty = DependencyProperty.Register(
                        "Opacity",
                        typeof(double),
                        typeof(GridElement),
                        new PropertyMetadata(1.0, new PropertyChangedCallback(OnOpacityPropertyChanged))
                    );

        /// <summary>
        /// Gets or sets opacity
        /// </summary>
        public double Opacity
        {
            get { return (double)GetValue(OpacityProperty); }
            set { SetValue(OpacityProperty, value); }
        }

        static protected void OnOpacityPropertyChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            DiagramElement el = d as DiagramElement;
            el.NotifyPropertyChanged("Opacity");
        }

        internal void AddAxisLine(Point begin, Point end, double value)
        {
            Line newAxisLine = new Line()
            {
                X1 = begin.X,
                Y1 = begin.Y,
                X2 = end.X,
                Y2 = end.Y,
                Tag = value
            };
            newAxisLine.SetBinding(Line.StrokeProperty, new Binding() { Source = AxisLineBrush });
            newAxisLine.SetBinding(Line.OpacityProperty, new Binding() { Source = Opacity });
            newAxisLine.SetBinding(Canvas.ZIndexProperty, new Binding() { Source = ZOrder });

            shapes.Add(newAxisLine);
            axisLine = newAxisLine;
        }

        internal void AddBasicLine(Point begin, Point end, double value)
        {
            Line newBasicLine = new Line()
            {
                X1 = begin.X,
                Y1 = begin.Y,
                X2 = end.X,
                Y2 = end.Y,
                Tag = value
            };
            newBasicLine.SetBinding(Line.StrokeProperty, new Binding() { Source = BasicLineBrush });
            newBasicLine.SetBinding(Line.OpacityProperty, new Binding() { Source = Opacity });
            newBasicLine.SetBinding(Canvas.ZIndexProperty, new Binding() { Source = ZOrder });

            shapes.Add(newBasicLine);
            basicLines.Add(newBasicLine);
        }

        internal void AddAuxLine(Point begin, Point end, double value)
        {
            Line newAuxLine = new Line()
            {
                X1 = begin.X,
                Y1 = begin.Y,
                X2 = end.X,
                Y2 = end.Y,
                Tag = value
            };
            newAuxLine.StrokeDashArray.Add(5);
            newAuxLine.StrokeDashArray.Add(2);
            newAuxLine.SetBinding(Line.StrokeProperty, new Binding() { Source = AuxLineBrush });
            newAuxLine.SetBinding(Line.OpacityProperty, new Binding() { Source = Opacity });
            newAuxLine.SetBinding(Canvas.ZIndexProperty, new Binding() { Source = ZOrder });

            shapes.Add(newAuxLine);
            auxLines.Add(newAuxLine);
        }

        internal override void ClearAllShapes()
        {
            shapes.Clear();
            auxLines.Clear();
            basicLines.Clear();
            axisLine = null;
            foreach (DiagramElement childElement in children)
            {
                childElement.ClearAllShapes();
            }
        }
    }
}
